package com.lc.midi.mid;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class MidiEvent {
	private int deltaTime;
	private byte state;
	private static byte oldState;
	private MidiEventType eventType;
	
	public int getDeltaTime() {
		return deltaTime;
	}

	public MidiEventType getEventType() {
		return eventType;
	}

	public Map<MidiEventMsg, String> getMessage() {
		return message;
	}
	
	public List<Byte> write(int deltaTime, MidiEventType eventType, List<Byte> data, MidiOutFile outFile){
		List<Byte> bytes = new ArrayList<Byte>();
		bytes.addAll(outFile.writeAutoNum(deltaTime));
		byte b = (byte)(((eventType.ordinal() + 8) << 4) + 0);
		bytes.add(b);
		bytes.addAll(data);
		return bytes;
	}

	private Map<MidiEventMsg, String> message = new HashMap<MidiEventMsg, String>();//事件的参数包含的信息
	public int read(MidiFile midiFile) throws IOException, MidiException {
		int ret;
		int returnLen = 0;
		byte[] tmp = new byte[1];
		deltaTime = midiFile.readAutoNum();
		returnLen = midiFile.getReadAutoNumLen();
		ret = midiFile.read(tmp);
		returnLen ++;
		state = tmp[0];
		if ((state & 0x80) == 0x00) {
			state = oldState;
		}else if((state & 0xf0) < 0xf0){
			oldState = state;
			ret = midiFile.read(tmp);
			returnLen++;
		}else {	//0xf0和0xff不提前读取一个字节
			switch (state) {
			case (byte) 0xf0:
				int paramLen = midiFile.readAutoNum();
				returnLen += midiFile.getReadAutoNumLen();
				tmp = new byte[paramLen];
				ret = midiFile.read(tmp);
				returnLen += paramLen;
				eventType = MidiEventType.systemCode;
				message.put(MidiEventMsg.systemCodeValue, ByteConvert.btoStr(tmp));
				return returnLen;
			case (byte) 0xff:
				eventType = MidiEventType.metaEvent;
				ret = readMetaEvent(midiFile);
				if (ret == -1)
					return -1;
				return returnLen + ret;
			case (byte) 0xf7://f7 Sysex event
				int paramLen2 = midiFile.readAutoNum();
				returnLen += midiFile.getReadAutoNumLen();
				tmp = new byte[paramLen2];
				ret = midiFile.read(tmp);
				returnLen += paramLen2;
				eventType = MidiEventType.sysexEvent;
				message.put(MidiEventMsg.f7msg, ByteConvert.btoStr(tmp));
				break;
			default:
				throw new MidiException("midi event state error:" + state);
			}
		}
		return returnLen + readEvent(midiFile, tmp[0]);
	}
	
	private int readEvent(MidiFile midiFile, byte param1) throws MidiException, IOException {
		byte[] tmp = new byte[1];
		int ret;
		eventType = MidiEventType.values()[((state&0xff) >> 4) - 8];
		message.put(MidiEventMsg.pass, ""+(state&0x0f));//状态字节低4位
		switch ((state&0xff) >> 4) {
		case 0x8:
			ret = midiFile.read(tmp);
			message.put(MidiEventMsg.keyCode, ""+(param1&0xff));
			message.put(MidiEventMsg.intensity, ""+(tmp[0]&0xff));
			break;
		case 0x9:
			ret = midiFile.read(tmp);
			if (tmp[0] == 0x00) {//力度为0表示松开音符而不是按下
				eventType = MidiEventType.noteOff;
			}
			message.put(MidiEventMsg.keyCode, ""+(param1&0xff));
			message.put(MidiEventMsg.intensity, ""+(tmp[0]&0xff));
			break;
		case 0xa:
			ret = midiFile.read(tmp);
			message.put(MidiEventMsg.keyCode, ""+(param1&0xff));
			message.put(MidiEventMsg.intensity, ""+(tmp[0]&0xff));
			break;
		case 0xb:
			ret = midiFile.read(tmp);
			message.put(MidiEventMsg.controlCode, ""+(param1&0xff));
			message.put(MidiEventMsg.controlParam, ""+(tmp[0]&0xff));
			break;
		case 0xc:
			message.put(MidiEventMsg.instrumentCode, ""+(param1&0xff));
			break;
		case 0xd:
			message.put(MidiEventMsg.passValue, ""+(param1&0xff));
			break;
		case 0xe:
			ret = midiFile.read(tmp);
			int soundHigh = (param1&0xff) + (tmp[0]&0xff) * 128;
			message.put(MidiEventMsg.soundHigh, ""+soundHigh);
			break;
		default:
			throw new MidiException("midi event state error:" + (state&0xff));
		}
		
		return 0;
	}
	
	private int readMetaEvent(MidiFile midiFile) throws IOException, MidiException {
		byte[] tmp = new byte[1];
		int ret = midiFile.read(tmp);
		byte metaCode = tmp[0];
		int paramLen = midiFile.readAutoNum();
		int returnLen = 1 + midiFile.getReadAutoNumLen();
		tmp = new byte[paramLen];
		ret = midiFile.read(tmp);
		returnLen += paramLen;
		message.put(MidiEventMsg.metaCode, ""+(metaCode&0xff));
		message.put(MidiEventMsg.metaData, ByteConvert.btoStr(tmp));
		switch ((int)metaCode) {
		case 0x00://设置轨道音序   不需要用到的就不处理
			
			break;
		case 0x2f:
			message.put(MidiEventMsg.endOfTrack, "1");
			return -1;
		case 0x51:	//设置速度
			if (paramLen != 3) {
				throw new MidiException("0xff 0x51: set speed error");
			}
			int speed = 0;
			for (int i = 0; i < paramLen; i++) {
				speed *= 256;
				speed += (tmp[i]&0xff);
			}
			message.put(MidiEventMsg.speed, ""+speed);
			break;
		default:
			break;
		}
		return returnLen;
	}
}
